<template>
    <div
        class="edit-wrapper"
        ref="editWrapper"
        :style="styles"
        :data-component-id="id"
        @mousedown="startMove"
        @click="onItemClick(id)"
        :class="{ active: active, hidden: hidden }"
    >
        <slot></slot>
        <div class="resizers">
            <div class="resizer top-left" @mousedown.stop="startResize('top-left')"></div>
            <div class="resizer top-right" @mousedown.stop="startResize('top-right')"></div>
            <div class="resizer bottom-left" @mousedown.stop="startResize('bottom-left')"></div>
            <div class="resizer bottom-right" @mousedown.stop="startResize('bottom-right')"></div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed, nextTick } from 'vue';
import { pick } from 'lodash-es';
type ResizeDirection = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
interface OriginalPositions {
    left: number;
    right: number;
    top: number;
    bottom: number;
}
export default defineComponent({
    props: {
        id: {
            type: String,
            required: true,
        },
        active: {
            type: Boolean,
            default: false,
        },
        hidden: {
            type: Boolean,
            default: false,
        },
        props: {
            type: Object,
        },
    },
    emits: ['set-active', 'update-position'],
    setup(props, context) {
        const editWrapper = ref<null | HTMLElement>(null);
        const onItemClick = (id: string) => {
            context.emit('set-active', id);
        };
        const gap = {
            x: 0,
            y: 0,
        };
        let isMoving = false;
        const styles = computed(() =>
            pick(props.props, ['position', 'top', 'left', 'width', 'height'])
        );
        const caculateMovePosition = (e: MouseEvent) => {
            const container = document.getElementById('canvas-area') as HTMLElement;
            const left = e.clientX - gap.x - container.offsetLeft;
            const top = e.clientY - gap.y - container.offsetTop + container.scrollTop;
            return {
                left,
                top
            }
        };
        const caculateSize = (
            direction: ResizeDirection,
            e: MouseEvent,
            positions: OriginalPositions
        ) => {
            const { clientX, clientY } = e;
            const { left, right, top, bottom } = positions;
            const container = document.getElementById('canvas-area') as HTMLElement;
            const rightWidth = clientX - left;
            const leftWidth = right - clientX;
            const bottomHeight = clientY - top;
            const topHeight = bottom - clientY;
            const topOffset = clientY - container.offsetTop + container.scrollTop;
            const leftOffset = clientX - container.offsetLeft;
            switch (direction) {
                case 'top-left':
                    return {
                        width: leftWidth,
                        height: topHeight,
                        top: topOffset,
                        left: leftOffset,
                    };
                case 'top-right':
                    return {
                        width: rightWidth,
                        height: topHeight,
                        top: topOffset,
                    };
                case 'bottom-left':
                    return {
                        width: leftWidth,
                        height: bottomHeight,
                        left: leftOffset,
                    };
                case 'bottom-right':
                    return {
                        width: rightWidth,
                        height: bottomHeight,
                    };
                default:
                    break;
            }
        };
        const startResize = (direction: ResizeDirection) => {
            const currentElement = editWrapper.value as HTMLElement;
            const { left, right, top, bottom } = currentElement.getBoundingClientRect();
            const handleMove = (e: MouseEvent) => {
                const size = caculateSize(direction, e, { left, right, top, bottom });
                const { style } = currentElement;
                if (size) {
                    style.width = size.width + 'px';
                    style.height = size.height + 'px';
                    if (size.left) {
                        style.left = `${size.left}px`;
                    }
                    if (size.top) {
                        style.top = `${size.top}px`;
                    }
                }
            };
            const handleMouseUp = (e: MouseEvent) => {
                document.removeEventListener('mousemove', handleMove);
                const size = caculateSize(direction, e, { left, right, top, bottom });
                context.emit('update-position', { ...size, id: props.id });
                nextTick(() => {
                    document.removeEventListener('mouseup', handleMouseUp);
                });
            };
            document.addEventListener('mousemove', handleMove);
            document.addEventListener('mouseup', handleMouseUp);
        };
        const startMove = (e: MouseEvent) => {
            const currentElement = editWrapper.value
            if (currentElement) {
                const { left, top } = currentElement.getBoundingClientRect()
                gap.x = e.clientX - left
                gap.y = e.clientY - top
            }
            const handleMove = (e: MouseEvent) => {
                const { left, top } = caculateMovePosition(e)
                isMoving = true
                if (currentElement) {
                    currentElement.style.top = `${top}px`
                    currentElement.style.left = `${left}px`
                }
            }
            const handleMouseUp = (e: MouseEvent) => {
                document.removeEventListener('mousemove', handleMove)
                if (isMoving) {
                    const { left, top } = caculateMovePosition(e)
                    context.emit('update-position', { left, top, id: props.id })
                    isMoving = false
                }
                nextTick(() => {
                    document.removeEventListener('mouseup', handleMouseUp)
                })
            }
            document.addEventListener('mousemove', handleMove)
            document.addEventListener('mouseup', handleMouseUp)
        }
        return {
            onItemClick,
            styles,
            editWrapper,
            startMove,
            startResize,
        };
    },
});
</script>

<style>
.edit-wrapper {
  padding: 0px;
  cursor: pointer;
  border: 1px solid transparent;
  user-select: none;
  box-sizing: content-box !important;
}
.edit-wrapper > * {
  position: static !important;
  width: 100% !important;
  height: 100% !important;
}
.edit-wrapper:hover {
  border: 1px dashed #ccc;
}
.edit-wrapper.hidden {
  display: none;
}
.edit-wrapper.active {
  border: 1px solid #1890ff;
  user-select: none;
  z-index: 1500;
}
.edit-wrapper .resizers {
  display: none;
}
.edit-wrapper.active .resizers {
  display: block;
}
.edit-wrapper.active .resizers .resizer {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  border: 3px solid #1890ff;
  position: absolute;
}
.edit-wrapper .resizers .resizer.top-left {
  left: -5px;
  top: -5px;
  cursor: nwse-resize;
}
.edit-wrapper .resizers .resizer.top-right {
  right: -5px;
  top: -5px;
  cursor: nesw-resize;
}
.edit-wrapper .resizers .resizer.bottom-left {
  left: -5px;
  bottom: -5px;
  cursor: nesw-resize;
}
.edit-wrapper .resizers .resizer.bottom-right {
  right: -5px;
  bottom: -5px;
  cursor: nwse-resize;
}
</style>
